POJ 2299 Ultra-QuickSort(求逆序数,归并排序或者离散化+树状数组)
Ultra-QuickSort
Time Limit: 7000MS | Memory Limit: 65536K | |
Total Submissions: 27665 | Accepted: 9915 |
Description
In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in ascending order. For the input sequence
9 1 0 5 4 ,
Ultra-QuickSort produces the output
0 1 4 5 9 .
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Ultra-QuickSort produces the output
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input
The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output
For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input
5 9 1 0 5 4 3 1 2 3 0
Sample Output
6 0
Source
本题就是求逆序数
一种方法是用归并排序求逆序数
/* POJ 2299 Ultra-QuickSort 求逆序数 用归并排序求逆序数 */ #include <stdio.h> #include<string.h> #include<iostream> using namespace std; const int MAXN=500010; long long ans;//存放逆序数,比较大,必须用long long int a[MAXN],b[MAXN],c[MAXN]; //将已经排好序的left~mid,mid+1~right进行归并 void merge(int *a,int left,int mid,int right) { int i,j; i=0; for(j=left;j<=mid;j++) b[i++]=a[j]; int len1=mid-left+1; i=0; for(j=mid+1;j<=right;j++) c[i++]=a[j]; int len2=right-mid; i=0; j=0; int k=left; while(i<len1&&j<len2&&k<=right) { if(b[i]<=c[j]) { a[k++]=b[i++]; } else { a[k++]=c[j++]; ans+=(len1-i);//逆序数就是累加后面比自己小的数的个数 //此时b[i]>c[j],那么c[j]会给b[i]后面的len1-i个数造成逆序数 } } while(i<len1) a[k++]=b[i++]; while(j<len2) a[k++]=c[j++]; } void merge_sort(int *a,int left,int right)//对a[left~right-1]进行归并排序 { if(left<right) { int mid=(left+right)/2; merge_sort(a,left,mid); merge_sort(a,mid+1,right); merge(a,left,mid,right); } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; while(scanf("%d",&n),n) { for(int i=0;i<n;i++) scanf("%d",&a[i]); ans=0; merge_sort(a,0,n-1); printf("%I64d\n",ans); } return 0; }
另外一种求逆序数的方法就是用树状数组。
本题数据比较大,需要先离散化,再用树状数组。
代码如下:
/* POJ 2299 Ultra-QuickSort 求逆序数 离散化+树状数组 首先从小到大进行编号,从而实现离散化 然后利用树状数组来统计每个数前面比自己大的数的个数 */ #include<stdio.h> #include<string.h> #include<algorithm> #include<iostream> using namespace std; const int MAXN=500010; int c[MAXN]; int b[MAXN]; int n; struct Node { int index;//序号 int v; }node[MAXN]; bool cmp(Node a,Node b) { return a.v<b.v; } int lowbit(int x) { return x&(-x); } void add(int i,int val) { while(i<=n) { c[i]+=val; i+=lowbit(i); } } int sum(int i) { int s=0; while(i>0) { s+=c[i]; i-=lowbit(i); } return s; } int main() { // freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d",&n),n) { for(int i=1;i<=n;i++) { scanf("%d",&node[i].v); node[i].index=i; } memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); //离散化 sort(node+1,node+n+1,cmp); //将最小的编号为1 b[node[1].index]=1; for(int i=2;i<=n;i++) { if(node[i].v!=node[i-1].v) b[node[i].index]=i; else b[node[i].index]=b[node[i-1].index]; } long long ans=0; //这里用的很好 //一开始c数组都是0,然后逐渐在b[i]处加上1; for(int i=1;i<=n;i++) { add(b[i],1); ans+=sum(n)-sum(b[i]); } printf("%I64d\n",ans); } return 0; }
人一我百!人十我万!永不放弃~~~怀着自信的心,去追逐梦想